package xerror

import (
	"bytes"
	"fmt"
	"io"

	. "gitee.com/shuohe/xutils"
)

func SimpleNew(msg string) error {
	return &Error{
		Cause: nil,
		Msg:   msg,
	}
}

func SimpleNewf(format string, v ...interface{}) error {
	return &Error{
		Cause: nil,
		Msg:   fmt.Sprintf(format, v...),
	}
}

func SimpleWrap(cause error, msg string) error {
	if cause == nil {
		return nil
	}

	return &Error{
		Cause: cause,
		Msg:   msg,
	}
}

func SimpleWrapf(cause error, format string, v ...interface{}) error {
	if cause == nil {
		return nil
	}

	return &Error{
		Cause: cause,
		Msg:   fmt.Sprintf(format, v...),
	}
}

func New(msg string) error {
	return &Error{
		Cause: nil,
		Msg:   Caller(2) + ": " + msg,
	}
}

func Newf(format string, v ...interface{}) error {
	return &Error{
		Cause: nil,
		Msg:   fmt.Sprintf(Caller(2)+": "+format, v...),
	}
}

func Wrap(cause error, msg string) error {
	if cause == nil {
		return nil
	}

	return &Error{
		Cause: cause,
		Msg:   Caller(2) + ": " + msg,
	}
}

func Wrapf(cause error, format string, v ...interface{}) error {
	if cause == nil {
		return nil
	}

	return &Error{
		Cause: cause,
		Msg:   fmt.Sprintf(Caller(2)+": "+format, v...),
	}
}

type Error struct {
	Cause error
	Msg   string
	Data  interface{}
}

func (err *Error) Error() string {
	return err.Msg
}

func (err *Error) Detail() string {
	buf := bytes.NewBuffer(make([]byte, 0, 1024))
	buf.WriteString(err.Msg)

	cause := err.Cause
	for cause != nil {
		buf.WriteString("\n\t")
		buf.WriteString(cause.Error())

		xe, ok := cause.(*Error)
		if ok {
			cause = xe.Cause
		} else {
			cause = nil
		}
	}

	return buf.String()
}

func (err *Error) Format(state fmt.State, verb rune) {
	switch verb {
	case 'v':
		io.WriteString(state, err.Detail())
	case 's':
		io.WriteString(state, err.Msg)
	case 'q':
		fmt.Fprintf(state, "%q", err.Msg)
	}
}
